package com.atguigu.gulimall.order.config;

import com.atguigu.common.contrsant.OrderConstant;
import com.atguigu.common.contrsant.SeckillConstant;
import com.atguigu.common.contrsant.WareConstant;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Binding.DestinationType;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitConfig {

    // 无法使用自动注入，会导致循环依赖
    RabbitTemplate rabbitTemplate;

    /**
     * 手动初始化
     * @param connectionFactory
     * @return
     */
    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter());
        initRabbitTemplate();
        return rabbitTemplate;
    }

    /**
     * 将对象序列化成JSON
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     * 1、服务收到消息就会回调
     *      1、spring.rabbitmq.publisher-confirms: true
     *      2、设置确认回调
     * 2、消息正确抵达队列就会进行回调
     *      1、spring.rabbitmq.publisher-returns: true
     *         spring.rabbitmq.template.mandatory: true
     *      2、设置确认回调ReturnCallback
     *
     * 3、消费端确认(保证每个消息都被正确消费，此时才可以broker删除这个消息)
     *
     */
//    @PostConstruct  // 对象创建完成以后，执行这个方法
    public void initRabbitTemplate() {

        /**
         * 设置broker收到消息的确认回调
         *
         * correlationData: 当前消息的唯一关联数据（消息的唯一id）
         * ack: Broker是否成功收到消息
         * cause: 失败的原因
         */
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            // 只要消息抵达Broker，ack就等于true，与消费者是否监听没有任何关系
            System.out.println("消息抵达Broker：correlationData：" + correlationData + "\tack：" + ack + "\tcause：" + cause);
        });

        /**
         *  只要消息没有成功到达队列，就触发这个失败回调
         *  测试方法：发送一个未指定的路由键即可
         *  message: 投递失败的详细信息
         *  replyCode: 回复的状态码
         *  replyText: 回复的文本内容
         *  exchange: 当时这个消息发给哪个交换机
         *  routingKey: 当时这个消息使用哪个路由键
         */
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            System.out.println("消息未抵达队列：" + message);
        });
    }

//    @RabbitListener(queues = OrderConstant.ORDER_RELEASE_ORDER_QUEUE)
//    public void listener(OrderEntity order, Channel channel, Message message) throws IOException {
//        System.out.println("收到过期的订单信息，准备关闭订单：" + order.getOrderSn());
//        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//    }

    /**
     * durable:是否持久化
     * autoDelete：是否自动删除
     * @return
     */
    @Bean
    public Exchange orderEventExchange() {
        TopicExchange exchange = new TopicExchange(OrderConstant.ORDER_EVENT_EXCHANGE, true, false);
        return exchange;
    }

    /**
     * 一旦创建好队列之后，再修改以下配置也不会覆盖掉已创建的队列
     * 除非手动删除 RabbitMQ 的队列，然后再重新运行一下代码
     *
     * durable:是否持久化
     * autoDelete：是否自动删除
     * exclusive:是否排它的（是否只能一个人连，谁抢到就是谁的）
     * @return
     */
    @Bean
    public Queue orderDelayQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", OrderConstant.ORDER_EVENT_EXCHANGE);//死性交换机：消息过期后传到的具体交换机
        args.put("x-dead-letter-routing-key", OrderConstant.ORDER_RELEASE_ORDER_ROUTING_KEY);//信死了，用的路由键是什么
        args.put("x-message-ttl", OrderConstant.MESSAGE_TTL);//ttl:过期时间
        Queue queue = new Queue(OrderConstant.ORDER_DELAY_QUEUE, true, false, false, args);

        return queue;
    }
    /**
     * exclusive:是否排它的（是否只能一个人连，谁抢到就是谁的）
     * @return
     */
    @Bean
    public Queue orderReleaseOrderQueue() {
        Queue queue = new Queue(OrderConstant.ORDER_RELEASE_ORDER_QUEUE, true, false, false);
        return queue;
    }

    /**
     * 秒杀订单队列
     * @return
     */
    @Bean
    public Queue orderSeckillOrderQueue() {
        Queue queue = new Queue(OrderConstant.ORDER_SECKILL_ORDER_QUEUE, true, false, false);
        return queue;
    }

    @Bean
    public Binding orderCreateOrderBinding() {
        Binding binding = new Binding(OrderConstant.ORDER_DELAY_QUEUE, DestinationType.QUEUE,
                OrderConstant.ORDER_EVENT_EXCHANGE,
                OrderConstant.ORDER_CREATE_ORDER_ROUTING_KEY, null);
        return binding;
    }

    @Bean
    public Binding orderReleaseOrderBinding() {
        Binding binding = new Binding(OrderConstant.ORDER_RELEASE_ORDER_QUEUE, DestinationType.QUEUE,
                OrderConstant.ORDER_EVENT_EXCHANGE,
                OrderConstant.ORDER_RELEASE_ORDER_ROUTING_KEY, null);
        return binding;
    }


    @Bean
    public Binding orderReleaseOtherBinding() {
        Binding binding = new Binding(WareConstant.STOCK_RELEASE_STOCK_QUEUE, DestinationType.QUEUE,
                OrderConstant.ORDER_EVENT_EXCHANGE,
                OrderConstant.ORDER_RELEASE_OTHER_BINDING, null);
        return binding;
    }

    /**
     * 秒杀队列绑定关系
     * @return
     */
    @Bean
    public Binding orderSeckillOrderBinding(){
        Binding binding = new Binding(OrderConstant.ORDER_SECKILL_ORDER_QUEUE, DestinationType.QUEUE,
                OrderConstant.ORDER_EVENT_EXCHANGE,
                OrderConstant.ORDER_SECKILL_ORDER_ROUTING_KEY, null);
        return binding;
    }

    @Bean
    public Binding orderSeckillReleaseOtherBinding() {
        Binding binding = new Binding(SeckillConstant.SECKILL_RELEASE_STOCK_QUEUE, DestinationType.QUEUE,
                OrderConstant.ORDER_EVENT_EXCHANGE,
                OrderConstant.ORDER_SECKILL_RELEASE_OTHER_BINDING, null);
        return binding;
    }
}
